#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <semaphore.h>

void *readthread_fun(void* arg);
void *writethread_fun(void* arg);
struct tag_WRITEWHOLE;
struct tag_READTHREAD;
typedef struct tag_WRITEWHOLE stWriteWhole;
typedef struct tag_READTHREAD stReadThread;
struct tag_READTHREAD
{
	pthread_t* pOneRead;
	int dest;			/*cannot be the same in each thread*/
	int beginIndex;
	stWriteWhole* pThisWrite;
	stReadThread* pstNext;
};
struct tag_WRITEWHOLE
{
	int id;
	pthread_t* pthreadThis;
	stReadThread* pstFirst;
	stReadThread* pstLast;
	char* pcCache;
	int frameNum;
	int clientNum;	/*important*/
	int *pNumOfThisPo; /*important*/
	sem_t* psem_tRead;
	sem_t* psem_tWrite;
	stWriteWhole* pnext;
};
typedef struct god
{
	stWriteWhole* pFirst;
	stWriteWhole* pLast;
	int num;
}stGOD;
stGOD GOD;
// pthread_t a_thread;

// sem_init(psem_t, 0, 0);

// pthread_create(&a_thread, NULL, thread_fun, NULL);

// sem_post(psem_t);

// sem_wait(psem_t);

// pthread_exit(NULL);


char readOne(int index, stWriteWhole* pThisWrite)
{
	char c;
	sem_t* parrsemread = pThisWrite->psem_tRead;
	sem_t* parrsemwrite = pThisWrite->psem_tWrite;
	printf("read thread gonna meet sem_wait\n");
	sem_wait(&parrsemread[index]);
	printf("[--+--+--+--+]\n");
	c = (pThisWrite->pcCache)[index];
	sem_post(&parrsemwrite[index]);
	return c;
}
void *readthread_fun(void* arg)
{
	int index = 0;
	char data = 0;
	sleep(5);
	stReadThread* pread = (stReadThread*)arg;
	stWriteWhole* pThisWrite = pread->pThisWrite;
	index = pread->beginIndex;
	printf("                                      begin index[%d]\n", index);
	while(1)
	{
		data = readOne(index, pThisWrite);
		index++;
		index = index % (pThisWrite->frameNum);
		printf("                                             [dest]:[%d]---->[%d]\n", pread->dest, data);
	}
	pthread_exit(NULL);
}
void WriteOne(int index, char* pData, int* pnumOfThisPo, sem_t* pWritesem_t, sem_t* pReadsem_t, int clientNum, stWriteWhole* pThis)
{
	int loop;
	static char lala = 0;
	printf("this time wait[%d]\n", *pnumOfThisPo);
	for(loop = 0; loop<*pnumOfThisPo; loop++)
	{
		sem_wait(pWritesem_t);
	}
	*pData = lala;
	lala++;
	for(loop = 0; loop<clientNum; loop++)
	{
		sem_post(pReadsem_t);
		printf("client:[%d]post[%d][%d]++++", clientNum, loop, index);
	}
	printf("\n");
	if(*pnumOfThisPo != clientNum && (pThis->pstLast)->beginIndex == -1)
	{
		(pThis->pstLast)->beginIndex = index;
		printf("this time index[%d]\n", index);
	}
	*pnumOfThisPo = clientNum;
	printf("next time should wait[%d]\n", *pnumOfThisPo);
	return;
}
void *writethread_fun(void* arg)
{
	stWriteWhole* pthisWrite = (stWriteWhole*)(arg);
	int index = 0;
	while(1)
	{
		sleep(1);
		WriteOne(index, &(pthisWrite->pcCache)[index], 		\
					&(pthisWrite->pNumOfThisPo)[index],		\
				 	&(pthisWrite->psem_tWrite)[index],		\
					&(pthisWrite->psem_tRead)[index],		\
					pthisWrite->clientNum,					\
					pthisWrite);
		printf("index:[%d]\n\n\n", index);	
		index++;
		index = index % pthisWrite->frameNum;
	}

	pthread_exit(NULL);
}

stWriteWhole* CreateWriteWhole(int id, int frameNum)
{
	stWriteWhole* pstThis;
	int loop;
	pstThis = malloc(sizeof(stWriteWhole));
	memset(pstThis, 0, sizeof(stWriteWhole));
	pstThis->id = id;
	pstThis->frameNum = frameNum;
	
	pstThis->pcCache = malloc(frameNum);
	memset(pstThis->pcCache, 0, frameNum);
	pstThis->clientNum = 0;
	pstThis->pNumOfThisPo = malloc(sizeof(int)*frameNum);
	memset(pstThis->pNumOfThisPo, 0, sizeof(int)*frameNum);
	pstThis->psem_tRead = malloc(sizeof(sem_t)*frameNum);
	memset(pstThis->psem_tRead, 0, sizeof(sem_t)*frameNum);
	pstThis->psem_tWrite = malloc(sizeof(sem_t)*frameNum);
	memset(pstThis->psem_tWrite, 0, sizeof(sem_t)*frameNum);
	for(loop = 0; loop<pstThis->frameNum; loop++)
	{
		sem_init((pstThis->psem_tRead)+loop, 0, 0);
		sem_init((pstThis->psem_tWrite)+loop, 0, 0);
	}
	pstThis->pthreadThis = malloc(sizeof(pthread_t));
	pthread_create(pstThis->pthreadThis, NULL, writethread_fun, (void*)pstThis);
	return pstThis;
}

void CreateReadThread(int id, int dest)
{
	// pthread_t* pOneRead;
	// int dest;			cannot be the same in each thread
	// st_ReadThread* pstNext;
	int isAdd = 0;
	stWriteWhole* ptmp = GOD.pFirst;
	stReadThread* pRead = malloc(sizeof(stReadThread));
	pRead->dest = dest;
	while(ptmp != 0)
	{
		if(ptmp->id != id)
		{
			ptmp = ptmp->pnext;
			continue;
		}
		pRead->pThisWrite = ptmp;
		if(ptmp->clientNum == 0)
		{
			ptmp->pstFirst = pRead;
			ptmp->pstLast = pRead;
		}
		else
		{
			(ptmp->pstLast)->pstNext = pRead;
		}
		pRead->beginIndex = -1;
		ptmp->clientNum++;
		isAdd = 1;
		break;
	}
	if(isAdd != 1)
	{
		free(pRead);
	}
	pRead->pOneRead = malloc(sizeof(pthread_t));
	pthread_create(pRead->pOneRead, NULL, readthread_fun, (void*)pRead);
	return;
}
void AddOneTerminal(stWriteWhole* p)
{
	if(GOD.num == 0)
	{
		GOD.pFirst = p;
		GOD.pLast = p;
	}
	else
	{
		(GOD.pLast)->pnext = p;
	}
	GOD.num++;
	return;
}
int main()
{
	sleep(1);

	memset(&GOD, 0, sizeof(GOD));
	stWriteWhole* pone = CreateWriteWhole(0, 10);
	AddOneTerminal(pone);
	sleep(2);
	CreateReadThread(0, 1);
	sleep(1000);

	return 0;
}